package com.ywu.file.sys.standard.service;

import com.ywu.boot.context.YwuContext;
import com.ywu.boot.context.YwuContextHolder;
import com.ywu.boot.exception.YwuRuntimeException;
import com.ywu.common.entity.PageList;
import com.ywu.common.page.PageService;
import com.ywu.common.result.CommonResult;
import com.ywu.common.utils.TimerIdGenerateUtil;
import com.ywu.file.sys.standard.config.AppConfig;
import com.ywu.file.sys.standard.config.MinIoConfig;
import com.ywu.file.sys.standard.data.YwuFileSysRepository;
import com.ywu.file.sys.standard.dto.YwuFileAppPermissionDto;
import com.ywu.file.sys.standard.dto.YwuFileSysDto;
import com.ywu.file.sys.standard.entity.MinioFileEntity;
import com.ywu.file.sys.standard.entity.YwuFIleTaskEntity;
import com.ywu.file.sys.standard.entity.YwuFileAppPermissionEntity;
import com.ywu.file.sys.standard.entity.YwuFileSysEntity;
import com.ywu.file.sys.standard.utils.FileUtils;
import com.ywu.file.sys.standard.utils.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.math.BigDecimal;
import java.util.*;

/**
 * @ClassName YwuFileSysService
 * @Description TODO
 * @Author GroundDemo
 * @Date 2024/8/2 22:00
 * @Version 1.0
 **/
@Service
@Slf4j
public class YwuFileSysServiceV2 {
    @Autowired
    private YwuFileAppPermissionService ywuFileAppPermissionService;

    @Autowired
    private YwuFIleTaskService taskService;

    @Autowired
    private YwuFileSysRepository repository;

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private FileUtils fileUtils;

    @Autowired
    private YwuMinIoService minIoService;

    @Autowired
    private MinIoConfig minIoConfig;

    private static final long KB_SIZE = 1024L;

    private static final long MB_SIZE = 1024L * 1024L;

    private static final long GB_SIZE = 1024L * 1024L * 1024L;

    private static final BigDecimal KB_SIZE_DECIMAL = new BigDecimal(KB_SIZE);

    private static final BigDecimal MB_SIZE_DECIMAL = new BigDecimal(MB_SIZE);

    private static final BigDecimal GB_SIZE_DECIMAL = new BigDecimal(GB_SIZE);

    /**
    * @Author GroundDemo
    * @Description 分页查询文件数据列表
    * @Date 10:52 2024/8/3
    * @Param [ywuFileSysDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> queryFilePageList(YwuFileSysDto ywuFileSysDto) {
        // 从当前上下文中获取appId
        YwuContext currentContext = YwuContextHolder.getCurrentContext();
        String appId = currentContext.getYwuContextApp().getAppId();
        ywuFileSysDto.setFileAppOwner(appId);
        // 查询分页数量
        int count = repository.queryPageCount(ywuFileSysDto);
        // 构建分页参数
        PageService.execPageStartAndEnd(ywuFileSysDto.getPageInfo());
        // 查询分页
        List<YwuFileSysEntity> entityList = repository.queryPageList(ywuFileSysDto);
        // 查询文件后缀信息
        Map<String, String> fileNameMapper = fileUtils.getFileTypeName();
        // 文件类型映射
        entityList.forEach(res -> {
            res.setFileTypeName(fileNameMapper.get(res.getFileType()));
            // 获取文件大小
            long fileSize = Long.parseLong(res.getFileSize());
            // 将文件大小重置单位
            if (fileSize >= GB_SIZE) {
                BigDecimal divide = new BigDecimal(fileSize).divide(GB_SIZE_DECIMAL, 2, BigDecimal.ROUND_HALF_EVEN);
                res.setFileSizeHuman(divide.toString() + "GB");
            } else if (fileSize >= MB_SIZE) {
                BigDecimal divide = new BigDecimal(fileSize).divide(MB_SIZE_DECIMAL, 2, BigDecimal.ROUND_HALF_EVEN);
                res.setFileSizeHuman(divide.toString() + "MB");
            } else {
                BigDecimal divide = new BigDecimal(fileSize).divide(KB_SIZE_DECIMAL, 2, BigDecimal.ROUND_HALF_EVEN);
                res.setFileSizeHuman(divide.toString() + "KB");
            }
        });
        PageList<YwuFileSysEntity> pageList = new PageList<>(ywuFileSysDto.getPageInfo().getPageIndex(),
                ywuFileSysDto.getPageInfo().getPageSize(), count, entityList);
        return CommonResult.success(pageList);
    }

    /**
    * @Author GroundDemo
    * @Description 文件上传
    * @Date 20:19 2024/8/9
    * @Param [file]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    @Transactional
    public CommonResult<Object> uploadFile(MultipartFile file, YwuFileSysDto ywuFileSysDto, YwuContext currentContext) {
        YwuContextHolder.setCurrentContext(currentContext);
        String appId = currentContext.getYwuContextApp().getAppId();
        // 获取文件后缀&校验文件后缀
        String fileType = fileUtils.getFileType(file.getOriginalFilename());
        // 上传文件
        MinioFileEntity minioFileEntity = minIoService.uploadFile(file);
        // 将文件的元信息写入数据库中
        YwuFileSysEntity ywuFileSysEntity = new YwuFileSysEntity();
        String fileId = TimerIdGenerateUtil.nextId();
        ywuFileSysEntity.setId(fileId);
        ywuFileSysEntity.setFileId(appConfig.getFileIdPrefix() + ywuFileSysEntity.getId());
        ywuFileSysEntity.setFileName(minioFileEntity.getFileName());
        ywuFileSysEntity.setFileType(fileType);
        ywuFileSysEntity.setFileSize(String.valueOf(file.getSize()));
        ywuFileSysEntity.setMinioObjectName(minioFileEntity.getMinioObjectName());
        ywuFileSysEntity.setFileStatus(YwuFileSysEntity.FILE_STATUS_FINISH);
        ywuFileSysEntity.setPartNum(0);
        ywuFileSysEntity.setPartTotal(0);
        ywuFileSysEntity.createInfo(currentContext.getYwuContextUser().getUserAccount());
        ywuFileSysEntity.setFileAppOwner(appId);
        if (Objects.isNull(ywuFileSysDto.getIsShare())) {
            ywuFileSysEntity.setIsShare(false);
        } else {
            ywuFileSysEntity.setIsShare(ywuFileSysDto.getIsShare());
        }
        repository.addFileInfo(ywuFileSysEntity);
        // 构建返回值
        String resDownloadUrl = appConfig.getFileDownloadUrlPrefix() + ywuFileSysEntity.getFileId();
        return CommonResult.success(resDownloadUrl);
    }

    /**
    * @Author GroundDemo
    * @Description 删除文件
    * @Date 22:05 2024/8/9
    * @Param [ywuFileSysDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    @Transactional
    public CommonResult<Object> deleteFile(YwuFileSysDto ywuFileSysDto) {
        // 根据fileId查询数据
        YwuFileSysEntity search = new YwuFileSysEntity();
        YwuContext currentContext = YwuContextHolder.getCurrentContext();
        String appId = currentContext.getYwuContextApp().getAppId();
        search.setFileAppOwner(appId);
        search.setFileId(ywuFileSysDto.getFileId());
        YwuFileSysEntity entity = repository.getEntity(search);
        if (Objects.isNull(entity)) {
            return CommonResult.error().setErrorCode("file.id.is.not.exists")
                    .setMessage("文件不存在");
        }
        // 先删除数据库中的数据
        YwuFileSysEntity ywuFileSysEntity = new YwuFileSysEntity();
        ywuFileSysEntity.setFileId(ywuFileSysDto.getFileId());
        repository.deleteFileInfoByFileId(ywuFileSysEntity);
        // 删除文件
        minIoService.deleteFile(entity.getMinioObjectName());
        return CommonResult.success(null);
    }

    /**
    * @Author GroundDemo
    * @Description 下载文件
    * @Date 16:28 2024/8/10
    * @Param [response, ywuFileSysDto]
    * @return void
    **/
    public void downloadFile(HttpServletResponse response, YwuFileSysDto ywuFileSysDto) {
        // 根据当前文件查询查询文件详情
        YwuContext currentContext = YwuContextHolder.getCurrentContext();
        String appId = currentContext.getYwuContextApp().getAppId();
        YwuFileSysEntity search = new YwuFileSysEntity();
        search.setFileId(ywuFileSysDto.getFileId());
        YwuFileSysEntity entity = repository.getEntity(search);
        if (Objects.isNull(entity)) {
            // 返回异常
            CommonResult<Object> result = CommonResult.error().setErrorCode("file.id.is.not.exists")
                    .setMessage("文件不存在");
            ResponseUtils.writeResponse(response, result);
            return;
        }
        // 当前文件是否为共享，如果非应用共享，判断当前文件的AppOwner是否与当前上下文中的AppId一致
        if (!entity.getIsShare() && !entity.getFileAppOwner().equals(appId)) {
            // 查询应用是否存在授权
            YwuFileAppPermissionDto ywuFileAppPermissionDto = new YwuFileAppPermissionDto();
            ywuFileAppPermissionDto.setPermissionAppId(appId);
            ywuFileAppPermissionDto.setSourceAppId(ywuFileSysDto.getFileAppOwner());
            CommonResult<List<YwuFileAppPermissionEntity>> resList = ywuFileAppPermissionService.getPermissionByAppId(ywuFileAppPermissionDto);
            if (CollectionUtils.isEmpty(resList.getData())) {
                // 返回异常
                CommonResult<Object> result = CommonResult.error().setErrorCode("file.is.not.share")
                        .setMessage("文件非共享");
                ResponseUtils.writeResponse(response, result);
                return;
            }
        }
        // 下载文件
        File file = null;
        try {
            minIoService.downloadFile(entity.getMinioObjectName(), minIoConfig.getDownloadTempDir() + File.separator + minIoConfig.getBucket() + File.separator + entity.getFileName());
            file = new File(minIoConfig.getDownloadTempDir() + File.separator + minIoConfig.getBucket() + File.separator + entity.getFileName());
            ResponseUtils.writeResponseFile(response, file, ywuFileSysDto.getType());
        } finally {
            if (file != null && file.exists()) {
                file.delete();
            }
        }
    }

    /**
    * @Author GroundDemo
    * @Description 根据文件ID修改是否共享
    * @Date 18:53 2024/8/11
    * @Param [ywuFileSysDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> updateIsShareByFileId(YwuFileSysDto ywuFileSysDto) {
        YwuFileSysEntity ywuFileSysEntity = new YwuFileSysEntity();
        ywuFileSysEntity.setFileId(ywuFileSysDto.getFileId());
        ywuFileSysEntity.setIsShare(ywuFileSysDto.getIsShare());
        ywuFileSysEntity.updateInfo(YwuContextHolder.getCurrentContext().getYwuContextUser().getUserAccount());
        repository.updateFileInfo(ywuFileSysEntity);
        return CommonResult.success(null);
    }

    /**
    * @Author GroundDemo
    * @Description 文件上传 - 分段式
    * @Date 18:11 2024/9/16
    * @Param [file, taskId, segmentId, fileName]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    @Transactional
    public CommonResult<Object> uploadFileMulti(MultipartFile file, String taskId, String segmentId) {
        // 根据fileId更新partNum数量
        log.debug("uploadFileMulti realFileName {}, fileSize is {}", file.getSize());
        // 根据taskId查询fileId
        YwuFIleTaskEntity search = new YwuFIleTaskEntity();
        search.setId(taskId);
        YwuFIleTaskEntity entity = taskService.getEntityById(search);
        // 根据fileId查询文件信息
        YwuFileSysEntity ywuFileSysEntity = repository.getEntityUsingUploadById(entity.getFileId());
        // 获取文件类型
        String fileType = fileUtils.getFileType(ywuFileSysEntity.getFileName());
        // 获取当前上传的文件类型
        String originFileType = fileUtils.getFileType(file.getOriginalFilename());
        if (!fileType.equals(originFileType)) {
            return CommonResult.error().setErrorCode("file.type.is.not.equals")
                    .setMessage("文件类型不一致");
        }
        // 临时文件存储路径
        String fileDir = fileUtils.generateFileDir() + File.separator + entity.getFileId();
        File tempFileDir = new File(fileDir);
        if (!tempFileDir.exists()) {
            boolean mkdirs = tempFileDir.mkdirs();
            if (!mkdirs && !tempFileDir.exists()) {
                throw new YwuRuntimeException("uploadFileMulti create dir error");
            }
        }
        // 写入文件
        String fileName = segmentId + ".tmp";
        String filePath = fileDir + File.separator + fileName;
        fileUtils.writeMultipartFile(file, filePath);
        if (StringUtils.isEmpty(ywuFileSysEntity.getPartSegment())) {
            ywuFileSysEntity.setPartSegment(segmentId);
        } else {
            ywuFileSysEntity.setPartSegment(ywuFileSysEntity.getPartSegment() + "," + segmentId);
        }
        repository.updatePartNumAndFileName(entity.getFileId(), ywuFileSysEntity.getPartSegment(), fileType);
        return CommonResult.success(null);
    }

    /**
    * @Author GroundDemo
    * @Description 分段式文件上传完成后，校验所有分段是否全部上传完毕
    * @Date 11:33 2024/9/16
    * @Param [ywuFileSysDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> uploadFileMultiSegmentCheck(YwuFileSysDto ywuFileSysDto) {
        // 根据taskId查询fileId
        YwuFIleTaskEntity search = new YwuFIleTaskEntity();
        search.setId(ywuFileSysDto.getTaskId());
        YwuFIleTaskEntity entity = taskService.getEntityById(search);
        // 根据fileId查询文件信息
        YwuFileSysEntity ywuFileSysEntity = repository.getEntityUsingUploadById(entity.getFileId());
        String partSegment = ywuFileSysEntity.getPartSegment();
        String[] segments = partSegment.split(",");
        Set<String> currentFileSegment = new HashSet<>(Arrays.asList(segments));
        Integer partTotal = ywuFileSysEntity.getPartTotal();
        List<String> resIds = new ArrayList<>();
        // 每个索引号多对应一个分段文件
        for (int index = 0; index < partTotal; index++) {
            String id = String.valueOf(index);
            if (!currentFileSegment.contains(id)) {
                resIds.add(id);
            }
        }
        YwuFileSysDto result = new YwuFileSysDto();
        result.setSegmentIds(resIds);
        return CommonResult.success(result);
    }

    /**
    * @Author GroundDemo
    * @Description 分段式文件上传合并最终文件
    * @Date 12:18 2024/9/16
    * @Param [ywuFileSysDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    @Transactional
    public CommonResult<Object> uploadFileMultiMerge(YwuFileSysDto ywuFileSysDto) {
        // 根据taskId查询fileId
        YwuFIleTaskEntity search = new YwuFIleTaskEntity();
        search.setId(ywuFileSysDto.getTaskId());
        YwuFIleTaskEntity entity = taskService.getEntityById(search);
        // 根据fileId查询文件信息
        String fileId = entity.getFileId();
        YwuFileSysEntity ywuFileSysEntity = repository.getEntityUsingUploadById(fileId);
        String partSegment = ywuFileSysEntity.getPartSegment();
        String[] segments = partSegment.split(",");
        // 对分段进行排序
        List<Integer> segmentInt = new ArrayList<>();
        for (String segment : segments) {
            segmentInt.add(Integer.parseInt(segment));
        }
        Collections.sort(segmentInt);
        log.debug("uploadFileMultiMerge segments is {}", Arrays.toString(segments));
        // 临时文件存储路径
        String fileDir = fileUtils.generateFileDir() + File.separator + fileId;
        String filePath = fileDir + File.separator + ywuFileSysEntity.getFileName();
        for (int segmentId : segmentInt) {
            String tempFilePath = fileDir + File.separator + segmentId + ".tmp";
            fileUtils.copyFile(tempFilePath, filePath, true);
        }
        // 更新文件的状态为FINISH
        repository.updateFileStatusById(fileId, YwuFileSysEntity.FILE_STATUS_FINISH, String.valueOf(new File(filePath).length()));
        // 更新task任务状态为已完成
        taskService.updateTask(ywuFileSysDto.getTaskId(), "", YwuFIleTaskEntity.TASK_STATE_FINISH);
        // 删除tmp数据
        for (int segmentId : segmentInt) {
            String tempFilePath = fileDir + File.separator + segmentId + ".tmp";
            fileUtils.deleteFile(tempFilePath);
        }
        // 上传文件至minio
        MinioFileEntity minioFileEntity = minIoService.uploadFile(ywuFileSysEntity.getFileName(), filePath);
        // 更新ObjectName
        repository.updateObjectName(fileId, minioFileEntity.getMinioObjectName());
        return CommonResult.success(null);
    }
}
